package net.moonbiter.ebs.protocols.httpjava;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.moonbiter.OperationFailureException;
import net.moonbiter.ebs.ServiceCaller;
import net.moonbiter.ebs.validation.UnexistingParamValidationException;
import net.moonbiter.ebs.validation.ValidationException;
import net.moonbiter.ebs.validation.WrongTypeValidationParamException;

public class JavaOnHttpCaller extends
		ServiceCaller<HttpServletRequest, HttpServletResponse> {

	@Override
	public void deliverError(HttpServletResponse output, Exception exception)
			throws IOException {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("error", exception);
		concreteDeliver(output, map);
	}

	private void concreteDeliver(HttpServletResponse output, Object obj)
			throws IOException {
		OutputStream os = null;
		ObjectOutputStream objOs = null;
		try {
			os = output.getOutputStream();
			objOs = new ObjectOutputStream(os);
			objOs.writeObject(obj);
			objOs.close();
		} finally {
			try {
				if (objOs != null) {
					objOs.close();
				}
			} finally {
				if (os != null) {
					os.close();
				}
			}
		}
	}

	@Override
	protected void deliverResult(HttpServletResponse output, Object result) throws IOException
			 {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("result", result);
		concreteDeliver(output, map);
	}

	@Override
	protected Map<String, Object> obtainParams(HttpServletRequest inputs,
			Map<String, Class<?>> requiredLogicalParams)
			throws ValidationException, OperationFailureException {
		InputStream is = null;
		ObjectInputStream objIs = null;
		try {
			is = inputs.getInputStream();
			objIs = new ObjectInputStream(is);
			Object rawInput = objIs.readObject();
			// verify raw input is a correct map
			if (!(rawInput instanceof Map)){
				throw new ValidationException("no map send");
			}
			Map<?,?> rawMap = (Map<?,?>)rawInput;
			// verificare tutte le chiavi per vedere se sono
			// stringhe
			for (Object aKey : rawMap.keySet()){
				if (!(aKey instanceof String)){
					throw new ValidationException("unvalid map send (no string keys present)");
				}
			}
			
			Map<String,Object> map = (Map<String,Object>)rawMap;			
			Map<String,Object> selectedMap = new HashMap<String,Object>();
			for (String reqParamName : requiredLogicalParams.keySet()){
				if (map.containsKey(reqParamName)){
					Object value = map.get(reqParamName);
					Class<?> reqClass = requiredLogicalParams.get(reqParamName); 
					if (reqClass.isInstance(value)){
						selectedMap.put(reqParamName, value);
					} else {
						throw new WrongTypeValidationParamException(reqParamName,
								reqClass.getCanonicalName(),
								value.getClass().getCanonicalName());
					}
				} else {
					throw new UnexistingParamValidationException(reqParamName);
				}
			}
			
			return selectedMap;
		} catch (ClassNotFoundException ex) {
			throw new OperationFailureException("obtaining input params", ex);
		} catch (IOException ex) {
			// cannot read a valid serialized object
			throw new ValidationException("request doesn't contain a valid serialized java object");
		} finally {
			try {
				if (objIs != null) {
					objIs.close();
				}
			} catch (IOException ex) {
				throw new OperationFailureException("obtaining input params",
						ex);
			} finally {
				if (is != null) {
					try {
						is.close();
					} catch (IOException ex) {
						throw new OperationFailureException(
								"obtaining input params", ex);
					}
				}
			}
		}

	}

}
